这篇文章中,描述了如何使用 json 作为交换格式实现 RPC 调用。本文设计的知识点为:

  • nlohmann_json

  • 函数类型萃取

  • 元组展开

函数类型萃取

这部分可以直接看代码:

template<typename T>
struct function_traits;

template<typename RetType, typename... Args>
struct function_traits<RetType(Args...)> {
public:
    // typedef Ret function_type(Args...);
    using function_type = RetType(Args...);
    using fcuntion_pointer = RetType (*)(Args...);
    using return_type = RetType;
    using bare_return_type = std::remove_const<std::remove_reference<return_type>>;
    using args_tuple = std::tuple<Args...>;
};

template<typename RetType, typename... Args>
struct function_traits<RetType (*)(Args...)> : function_traits<RetType(Args...)> { };

// 成员函数
template<typename RetType, typename ClassType, typename... Args>
struct function_traits<RetType (ClassType::*)(Args...)> : function_traits<RetType(Args...)> { };

template<typename RetType, typename ClassType, typename... Args>
struct function_traits<RetType (ClassType::*)(Args...) const> : function_traits<RetType(Args...)> { };

元组展开

元组展开一般用做给表达式传参,在 lua 中语法类型为:

local b = {1, 2, 3}
f(b...) --- 相当于调用 b(1, 2, 3)

同样,在 Python 中也有类似的语法:

b = [1, 2, 3]
f(*b) # 相当于调用 f(1, 2, 3)

在 C++ 14 中,可以将元组参数使用 std::index_sequence 展开: [1]

template<typename Func, typename Tuple, size_t ... I>
auto call(Function f, Tuple t, std::index_sequence<I...>){
    return f(std::get<I>(t)...);
}

template<typename Func, typename Tuple>
auto call(Function f, Tuple t){
    static constexpr auto size = std::tuple_size<Tuple>::value;
    return call(f, t, std::make_index_sequence<size>{});
}

在 C++ 17 中,具备更简单的方法:

auto f = [](int a, double b, std::string c){}
auto params = std::make_tuple(1, 2.0f, "hello");
std::apply(f, params);

实现


//  请求函数
template<typename... Args>
nlohmann::json RequestFunction(std::string const& function_name, Args&&... args) {
    // clang-format off
    return {
        { "ProcName", function_name },
        { "Params", std::make_tuple(args...) }
    };
   //clang-format on
}

// 注册函数
template<typename ObjFunc, size_t... I>
typename function_traits<ObjFunc>::return_type call_helper(ObjFunc f, nlohmann::json const& t,
                                                           std::index_sequence<I...>) {
    return f(t[I]...);
}

template<typename ObjFunc, typename Obj, size_t... I>
typename function_traits<ObjFunc>::return_type call_helper(ObjFunc f, Obj& obj, nlohmann::json const& t,
                                                           std::index_sequence<I...>) {
    return (obj.*f)(t[I]...); // 成员函数
}
template<typename ObjFunc>
typename function_traits<ObjFunc>::return_type
CallFunction(ObjFunc f, nlohmann::json const& t) {
    using args_tuple = typename function_traits<ObjFunc>::args_tuple;

    return call_helper(f, t, std::make_index_sequence<std::tuple_size<args_tuple>::value>());
}

template<typename ObjFunc, typename Obj>
typename function_traits<ObjFunc>::return_type
CallFunction(ObjFunc f, Obj& obj, nlohmann::json const& t) {
    using args_tuple = typename function_traits<ObjFunc>::args_tuple;
    // 成员函数
    return call_helper(f, obj, t, std::make_index_sequence<std::tuple_size<args_tuple>::value>());
}

测试

测试代码如下:

#include <functional>
#include <iostream>
#include <nlohmann/json.hpp>
#include <string>
#include <tuple>

#include "function_traits.hpp"
#include "remotecall.hpp"

double Devide2(int b) {
    return b / 2.f;
}

struct AddNumber {

    AddNumber(int num) : num_(num) { }
    int GetResult(int b) {
        return num_ + b;
    }

private:
    int num_;
};

void printNumber(int a) {
    printf("%d\n", a);
}

int get12() {
    return 12;
}


int main(int argc, char** argv) {
    auto devide2 = RequestFunction("Devide2", 10);
    printf("%s\n", devide2.dump().c_str());
    std::cout << CallFunction(Devide2, devide2["Params"])<<std::endl;

    AddNumber obj(10);
    auto json_obj = RequestFunction("AddNumber::GetResult", 10);
    std::cout << CallFunction(&AddNumber::GetResult, obj, json_obj["Params"])<<std::endl;

    std::cout<<CallFunction(&AddNumber::GetResult, AddNumber(20), json_obj["Params"])<<std::endl;

    CallFunction(&printNumber, devide2["Params"]);

    printf("%d\n", CallFunction(&get12, RequestFunction("get12")["Params"]));
    return 0;
}
Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx